#include <Arduino.h>
#include <Adafruit_NeoPixel.h>

#define PIXEL_TYPE uint32_t

#define RGB_Pin 2 //RGB引脚
#define RGB_Num 34  //RGB灯数量------------------------在这里调整------------------------------


#define RColor 0xff0000//红色
#define BColor 0x0000ff//蓝
#define YColor 0xffff66//黄
#define PColor 0xcc33cc//紫
#define GColor 0x00ff00//绿
#define CColor 0x33ffff//青
#define OColor 0xff9966//橙

uint32_t Mode1_ColorTab[7]={RColor,BColor,YColor,PColor,GColor,CColor,OColor};
union RGB_Typ{//自定义rgb颜色联合体
  struct{
    uint8_t BValue;
    uint8_t GValue;
    uint8_t RValue;
    uint8_t Blightness;
  }sub;
  uint32_t Value;
};

uint32_t sysTimCnt;
uint32_t sysTimCntBak;
uint8_t Tim50msCnt;
uint8_t Tim500msCnt;
bool Tim500msIF;

Adafruit_NeoPixel theRGBLine = Adafruit_NeoPixel(RGB_Num,RGB_Pin,NEO_GRB + NEO_KHZ800);

void setup(){
  theRGBLine.begin();
  randomSeed(analogRead(A0));
  Serial.begin(9600);
}
/*
 * function:OnePoint_3Cricle. Just one on-led in every cricle.
 * offset: the offset led of a line
 * gaps:  the numbers of off-led. In the video it shows 2
 * color:  the color set to on-led
*/
void OnePoint_3Cricle(int offset, int gaps, int points, PIXEL_TYPE color){ //一点-八点
  offset = offset % (gaps + points);
  uint8_t i = 0;
  for (; (i + gaps) < offset; i++){//light leds before offset
    theRGBLine.setPixelColor(i, color);
  }
  for (; i < offset; i++){//off leds before offset
    theRGBLine.setPixelColor(i, 0);
  }
  for (; i < RGB_Num; i++){//ctrl leds after offset
    for (uint8_t j = 0; j < points; j++){ //light leds after next cricle
      theRGBLine.setPixelColor(i, color);
      i++;
    }
    for (uint8_t j = 0; j < gaps; j++){//off leds after next cricle
      theRGBLine.setPixelColor(i, 0);
      i++;
    }
  }
  theRGBLine.show();  //show led line, you should call your own implemention
}

//points = 9, speed = 200
void Simple_Marquee(int offset, int points, PIXEL_TYPE color){//跑马灯
  offset = offset % RGB_Num;
  uint8_t i = 0;
  for (; (i + points) <= offset; i++){
    theRGBLine.setPixelColor(i, 0);
  }
  for (; i <= offset; i++){
    theRGBLine.setPixelColor(i, color);
  }
  for (; i < RGB_Num; i++){
    theRGBLine.setPixelColor(i, 0);
  }
  theRGBLine.show();  //show led line, you should call your own implemention
}

//gaps = 4, points = 6, speed = 200
void Mutil_Marquee(int offset, int gaps, int points, PIXEL_TYPE color){ //高密度跑马灯
  offset = offset % (gaps + points);
  uint8_t i = 0;
  for (; (i + gaps) < offset; i++){//light leds before offset
    theRGBLine.setPixelColor(i, color);
  }
  for (; i < offset; i++){//off leds before offset
    theRGBLine.setPixelColor(i, 0);
  }
  for (; i < RGB_Num; i++){//ctrl leds after offset
    for (uint8_t j = 0; j < points; j++){ //light leds after next cricle
      theRGBLine.setPixelColor(i, color);
      i++;
    }
    for (uint8_t j = 0; j < gaps; j++){//off leds after next cricle
      theRGBLine.setPixelColor(i, 0);
      i++;
    }
  }
  theRGBLine.show();  //show led line, you should call your own implemention
}

#define Marquee_Points 8
uint32_t ColorTab[Marquee_Points] = {0};
void Simple_Center_Marquee(int offset, int points, PIXEL_TYPE color){//居中跑马灯
  static uint32_t colorBak = 0;
  int CenterPoint = (RGB_Num - 1) >> 1;
  int TotalNum = (CenterPoint + 1) << 1;
  offset = offset % (CenterPoint + points + 1);

  if(colorBak != color){  // update color table
    colorBak = color;
    uint8_t RStep = (color >> 16) / points;
    uint8_t GStep = ((color >> 8) & 0x00ff) / points;
    uint8_t BStep = (color & 0x0000ff) / points;
    uint32_t Step = (RStep << 16) + (GStep << 8) + BStep;
    for (uint8_t i = points; i > 0; i--){  //渐变颜色列表，
      ColorTab[i-1] = color;
      color = color - Step;
    }
  }

  uint8_t i = 0;
  for (; (i + points) <= offset; i++){
    theRGBLine.setPixelColor(i, 0);
    uint8_t j = TotalNum - i -1;
    theRGBLine.setPixelColor(j, 0);
  }
  uint8_t cIdx = 0;
  for (; i <= offset; i++){
    uint8_t j = TotalNum - i -1;
    if(offset < points){
      theRGBLine.setPixelColor(i, ColorTab[Marquee_Points - 1 - offset + cIdx]);
      theRGBLine.setPixelColor(j, ColorTab[Marquee_Points - 1 - offset + cIdx]);
    }
    else if(i <= CenterPoint){
      theRGBLine.setPixelColor(i, ColorTab[cIdx]);
      theRGBLine.setPixelColor(j, ColorTab[cIdx]);
    }
    cIdx++;
  }
  theRGBLine.show();  //show led line, you should call your own implemention
}
    // Mutil_Center_Marquee(OffSet, 3, 5, 0x090909);

bool NextCricle = false;//3,5
void Mutil_Center_Marquee(int offset, int gaps, int points, PIXEL_TYPE color){//高密度居中跑马
  static uint32_t colorBak = 0;
  int CenterPoint = (RGB_Num - 1) >> 1;
  int TotalNum = (CenterPoint + 1) << 1;
  int finalPoint = CenterPoint + points;
  if(offset > finalPoint) NextCricle = true;
  if(NextCricle){
    if (offset <= finalPoint){
      if(offset <= (CenterPoint+1)%(gaps+points)+points)
        offset = CenterPoint + offset;
      else
        offset = offset + CenterPoint;
    }
    if(offset > finalPoint){
      offset = (offset - finalPoint - 1) % (gaps + points) + (CenterPoint - gaps + 1);
    }
    // Serial.print(offset);
    // Serial.print(",");
    // if(offset == finalPoint)
    //   Serial.println();
  }
  else{
    offset = offset % (finalPoint + 1);
    // Serial.print(offset);
    // Serial.print(",");
    // if(offset == finalPoint)
    //   Serial.println();
  }
  uint8_t totalCricle = (offset + 1) / (gaps + points); //整圈数量
  uint8_t restNum = (offset + 1) % (gaps + points);     //余数

  if(colorBak != color){  // update color table
    colorBak = color;
    uint8_t RStep = (color >> 16) / points;
    uint8_t GStep = ((color >> 8) & 0x00ff) / points;
    uint8_t BStep = (color & 0x0000ff) / points;
    uint32_t Step = (RStep << 16) + (GStep << 8) + BStep;
    for (uint8_t i = 0; i < points; i++){  //渐变颜色列表
      ColorTab[i] = color;
      color = color - Step;
    }
  }

  for (uint8_t i = 0; i < totalCricle; i++){
    for (uint8_t j = 0; j < points; j++){
      uint8_t k = TotalNum - offset - 1;
      if(offset <= CenterPoint){
        theRGBLine.setPixelColor(offset, ColorTab[j]);
        theRGBLine.setPixelColor(k, ColorTab[j]);
      }
      offset--;
    }
    for (uint8_t j = 0; j < gaps; j++){
      uint8_t k = TotalNum - offset - 1;
      theRGBLine.setPixelColor(offset, 0);
      theRGBLine.setPixelColor(k, 0);
      if(offset==0) break;
      offset--;
    }
  }
  for (uint8_t i = 0; i < restNum; i++){
    uint8_t k = TotalNum - offset - 1;
    if(i < points){
      theRGBLine.setPixelColor(offset, ColorTab[i]);
      theRGBLine.setPixelColor(k, ColorTab[i]);
    }
    else{
      theRGBLine.setPixelColor(offset, 0);
      theRGBLine.setPixelColor(k, 0);  
    }
    offset--;
  }
  theRGBLine.show();  //show led line, you should call your own implemention
}

void Random_Flash(int points, PIXEL_TYPE color){//随机慢闪、乱闪
    theRGBLine.clear();
    for (uint8_t i = 0; i < points;)
    {
      int k = random(0, RGB_Num);
      if (theRGBLine.getPixelColor(k) == 0)
      {
        theRGBLine.setPixelColor(k, color);
        i++;
      } //no repeat
    }
    theRGBLine.show();
}

#define FILL_MAX_POINTS 20
int startArray[FILL_MAX_POINTS] = {0};//seed array    
int resultArray[FILL_MAX_POINTS] = {0};//random array    
bool Fill_Color_Init = true;  // should set true when first time call Fill_Color fun

void Fill_Init(PIXEL_TYPE fillcolor){
  Fill_Color_Init = true;
  theRGBLine.fill(fillcolor); 
  theRGBLine.show();
}

bool Fill_Color(int points, PIXEL_TYPE color){//true means fill cell finish，填空，填满
  int totalCricle = RGB_Num/points;
  int restNum = RGB_Num % points;
  static int resultIdx = 0;
  if(Fill_Color_Init){    //create random array
    Fill_Color_Init = false;
    for (uint8_t i = 0; i < points; i++){
      startArray[i] = i;
    }
    for(int i = 0; i < points; i++){
      int seed = random(0, points - i);
      // Serial.print(startArray[seed]);
      // Serial.print(",");
      resultArray[i] = startArray[seed];//save random result without repeat
      startArray[seed] = startArray[points - i - 1];
    }
    // Serial.println();
    // if(color){
    //   theRGBLine.clear(); //clear color when init
    // }
    // else{
    //   theRGBLine.fill(fillcolor); //fill color in red when init
    // }
    // theRGBLine.show();
    // return Fill_Color_Init;
  }

  uint8_t i=0;
  for(;i<totalCricle;i++){
    theRGBLine.setPixelColor(resultArray[resultIdx]+points*i, color);
  }
  if(restNum>resultArray[resultIdx]){
    theRGBLine.setPixelColor(totalCricle*points+resultArray[resultIdx], color);
  }
  resultIdx++;
  if(resultIdx>=points){
    Fill_Color_Init = true;
    resultIdx = 0;
  }

  theRGBLine.show();
  return Fill_Color_Init;//return true means fill fully, otherwise you should keep calling this fun
}

uint8_t OffSet = 0;
void loop(){
  if(Tim500msIF){
    Tim500msIF = false;
    // OnePoint_3Cricle(OffSet,3,8,1);
    // Simple_Marquee(OffSet, 6, 2);
    // Mutil_Marquee(OffSet, 3, 9, 0x010000);
    // Simple_Center_Marquee(OffSet, 8, 0x090909);
    // Mutil_Center_Marquee(OffSet, 3, 8, 0x090909);
    // Random_Flash(12, 0x0f0f00);
    // OffSet++;
    // if(Fill_Color(12, 0x0f0000)){
      // delay(1000);
    //   // Fill_Color_Init = true;
    //   Fill_Init(0);
    // }
    if(Fill_Color(12, 0)){
      // Fill_Color_Init = true;
      delay(1000);
      Fill_Init(0x0f0f00);
    }
  }
  sysTimCnt = micros();
  if(sysTimCnt<sysTimCntBak){
    sysTimCnt = sysTimCnt + (0xffffffff - sysTimCntBak);
    sysTimCntBak = 0;
  }
  if(sysTimCnt-sysTimCntBak>1000){//1ms
    if(++Tim50msCnt>50){//50ms定时
      Tim50msCnt = 0;
      if(++Tim500msCnt>=5){
        Tim500msCnt = 0;
        Tim500msIF = true;
      }
    }
    sysTimCntBak = micros();
  }
}